/***********************贪吃蛇 linux***************/
/**************************************************/
/***************create by C_9999 ******************/
/************contact  Info: 847402588@qq.com*******/
/**************************************************/

#include"common.h"
#include<curses.h>
#include<stdlib.h>
#include<signal.h>
#include<sys/time.h>
#include<string.h>
enum{
    UP,
    DOWM,
    LEFT,
    RIGHT
};
struct st_snake
{
    u2 u2_line;
    u2 u2_cols;
    u1 u1_length;
    struct st_snake* st_snakeNext;
    struct st_snake* st_snakePrev;
};
/***********static varaible********************/
struct st_snake* sst_snakeHead;
struct itimerval st_tmVal;
u2 static su2_foodLine;
u2 static su2_foodCols;
u1 static su1_moveStatus;
/***********static function********************/
void fs_init();
void fs_close_scr();
void fs_show_food();
void fs_snake_init();
void fs_snake_automove(s4 ts4_n); /*** called  by signal*****/
void fs_snake_moveLeft();
void fs_snake_moveRight();
void fs_snake_moveUp();
void fs_snake_moveDowm();
void fs_snake_add();
u1 fs_judge();
/**functioan**/

/**********main functioan***************/
int main()
{

    u1 u1_ch;
    srand((unsigned)time(NULL));
    
    fs_init();
    curs_set(0);
    fs_snake_init();
    fs_show_food();
    refresh();
    signal(SIGALRM,fs_snake_automove);
    
    /**enter the moving direction**/
    while(1)
    {
        u1_ch = getchar();
        switch(u1_ch)
        {
            case 'w':
                if(su1_moveStatus != DOWM)
                {
                    su1_moveStatus = UP;
                }
                break;
                
            case 'd':
                if(su1_moveStatus != LEFT)
                {
                    su1_moveStatus = RIGHT;
                }
                break;
                
            case 'a':
                if(su1_moveStatus != RIGHT)
                {
                    su1_moveStatus = LEFT;
                }
                break;
            case 's':
                if(su1_moveStatus != UP)
                {
                    su1_moveStatus = DOWM;
                }
                break;
            default:
                break;
            
        }
    }
    
    fs_close_scr();
    return 0;
}
void fs_init()
{
    initscr();
    
    memset(&st_tmVal,0,sizeof(st_tmVal));
    st_tmVal.it_value.tv_sec = 0;
    st_tmVal.it_value.tv_usec = 250000;
    st_tmVal.it_interval.tv_sec = 0;
    st_tmVal.it_interval.tv_usec = 250000;
    setitimer (ITIMER_REAL, &st_tmVal, 0); 
    
    /****** init the food ********/
    su2_foodLine = random()%LINES;
    su2_foodCols = random()%COLS;

}
void fs_show_food()
{    
    mvaddch(su2_foodLine,su2_foodCols,'*');
    refresh();
    return;
}
void fs_snake_init()
{
    u1 tu1_i;
    struct st_snake* tpt_st_snake;
    struct st_snake* tpt_st_snakeT;
    
    su1_moveStatus = LEFT;
    
    sst_snakeHead = (struct st_snake*)malloc(1);
    sst_snakeHead->st_snakeNext = NULL;
    sst_snakeHead->st_snakePrev = NULL;
    sst_snakeHead->u1_length = 3;
    sst_snakeHead->u2_line = LINES/2;
    sst_snakeHead->u2_cols = COLS/2;
    refresh();
    mvaddch(sst_snakeHead->u2_line,sst_snakeHead->u2_cols,'@');
    refresh();
    tpt_st_snakeT = sst_snakeHead;
    for(tu1_i=0; tu1_i<sst_snakeHead->u1_length-1 ;tu1_i++)
    {
        
        tpt_st_snake = (struct st_snake*)malloc(1);
        tpt_st_snake->u2_line = tpt_st_snakeT->u2_line;
        tpt_st_snake->u2_cols = (tpt_st_snakeT->u2_cols) + 1;
        tpt_st_snake->st_snakeNext = NULL;
        tpt_st_snake->st_snakePrev = tpt_st_snakeT;

        mvaddch(tpt_st_snake->u2_line,tpt_st_snake->u2_cols,'*');
        refresh();

        tpt_st_snakeT->st_snakeNext = tpt_st_snake;
        tpt_st_snakeT = tpt_st_snake;
    }
    return;

}
void fs_snake_automove(s4 ts4_n)
{
    u1 tu1_move_status;
    tu1_move_status = su1_moveStatus;
    clear();
    fs_show_food();

    switch(tu1_move_status)
    {
        case UP:
            fs_snake_moveUp();
            break;
            
        case DOWM:
            fs_snake_moveDowm();
            break;
                    
        case LEFT:
            fs_snake_moveLeft();

            break;
            
        case RIGHT:
            fs_snake_moveRight();

            break;
            
        default:
            break;
    }
    if(fs_judge())
    {
        fs_close_scr();
        exit(0);
    }
    return;
}
void fs_snake_moveLeft()
{
    u1 tu1_i;
    u2 tu2_line;
    u2 tu2_cols;
    struct st_snake* tpt_st_snake;
    tpt_st_snake = sst_snakeHead;
    
    /***draw the snake head***/
    mvaddch(tpt_st_snake->u2_line,tpt_st_snake->u2_cols-1,'@');
    /***find the snake tail***/
    while(tpt_st_snake->st_snakeNext != NULL)
    {
        tpt_st_snake = tpt_st_snake->st_snakeNext;
    }
    /**Node => prev Node**/
    while(tpt_st_snake->st_snakePrev !=NULL)
    {
        tpt_st_snake->u2_line = (tpt_st_snake->st_snakePrev)->u2_line;
        tpt_st_snake->u2_cols = (tpt_st_snake->st_snakePrev)->u2_cols;
        mvaddch(tpt_st_snake->u2_line,tpt_st_snake->u2_cols,'*');
        tpt_st_snake = tpt_st_snake->st_snakePrev;
    }
    
    sst_snakeHead->u2_cols = sst_snakeHead->u2_cols - 1;
    refresh();
    return;
}
void fs_snake_moveUp()
{
    u1 tu1_i;
    u2 tu2_line;
    u2 tu2_cols;
    struct st_snake* tpt_st_snake;
    tpt_st_snake = sst_snakeHead;
    /***draw the snake head***/
    mvaddch(tpt_st_snake->u2_line-1,tpt_st_snake->u2_cols,'@');
    /***find the snake tail***/
    while(tpt_st_snake->st_snakeNext != NULL)
    {
        tpt_st_snake = tpt_st_snake->st_snakeNext;
    }
    /*Node => prev Node*/
    while(tpt_st_snake->st_snakePrev !=NULL)
    {
        tpt_st_snake->u2_line = (tpt_st_snake->st_snakePrev)->u2_line;
        tpt_st_snake->u2_cols = (tpt_st_snake->st_snakePrev)->u2_cols;
        refresh();
        mvaddch(tpt_st_snake->u2_line,tpt_st_snake->u2_cols,'*');
        tpt_st_snake = tpt_st_snake->st_snakePrev;
    }
    
    sst_snakeHead->u2_line = sst_snakeHead->u2_line - 1;
    refresh();
    return;
}
void fs_snake_moveRight()
{
    u1 tu1_i;
    u2 tu2_line;
    u2 tu2_cols;
    struct st_snake* tpt_st_snake;
    tpt_st_snake = sst_snakeHead;
    /*draw the snake head*/
    mvaddch(tpt_st_snake->u2_line,tpt_st_snake->u2_cols+1,'@');
    /*find the snake tail*/
    while(tpt_st_snake->st_snakeNext != NULL)
    {
        tpt_st_snake = tpt_st_snake->st_snakeNext;
    }
    /*Node => prev Node*/
    while(tpt_st_snake->st_snakePrev !=NULL)
    {
        tpt_st_snake->u2_line = (tpt_st_snake->st_snakePrev)->u2_line;
        tpt_st_snake->u2_cols = (tpt_st_snake->st_snakePrev)->u2_cols;
        refresh();
        mvaddch(tpt_st_snake->u2_line,tpt_st_snake->u2_cols,'*');
        tpt_st_snake = tpt_st_snake->st_snakePrev;
    }
    
    sst_snakeHead->u2_cols += 1;
    refresh();
    return;
}
void fs_snake_moveDowm()
{
    u1 tu1_i;
    u2 tu2_line;
    u2 tu2_cols;
    struct st_snake* tpt_st_snake;
    tpt_st_snake = sst_snakeHead;
    /*draw the snake head*/
    mvaddch(tpt_st_snake->u2_line+1,tpt_st_snake->u2_cols,'@');
    /*find the snake tail*/
    while(tpt_st_snake->st_snakeNext != NULL)
    {
        tpt_st_snake = tpt_st_snake->st_snakeNext;
    }
    /*Node => prev Node*/
    while(tpt_st_snake->st_snakePrev !=NULL)
    {
        tpt_st_snake->u2_line = (tpt_st_snake->st_snakePrev)->u2_line;
        tpt_st_snake->u2_cols = (tpt_st_snake->st_snakePrev)->u2_cols;
        refresh();
        mvaddch(tpt_st_snake->u2_line,tpt_st_snake->u2_cols,'*');
        tpt_st_snake = tpt_st_snake->st_snakePrev;
    }
    
    sst_snakeHead->u2_line = sst_snakeHead->u2_line +1;
    refresh();
    return;
}
void fs_snake_add()
{
    struct st_snake* tpt_st_snakeNew;
    struct st_snake* tpt_st_snake;
    
    tpt_st_snakeNew = (struct st_snake*) malloc(1);
    tpt_st_snake = sst_snakeHead;
    while(tpt_st_snake->st_snakeNext != NULL)
    {
        tpt_st_snake = tpt_st_snake->st_snakeNext;
    }
    tpt_st_snakeNew->st_snakePrev = tpt_st_snake;
    tpt_st_snakeNew->st_snakeNext = NULL;
    tpt_st_snake->st_snakeNext = tpt_st_snakeNew;
    
    /*reput the food*/
    su2_foodCols = random()%COLS;
    su2_foodLine = random()%LINES;
    
    return;
}
void fs_close_scr()
{
    struct st_snake* tpt_st_snake;
    struct st_snake* tpt_st_snakeT;
    
    tpt_st_snake = sst_snakeHead;
    while(tpt_st_snake->st_snakeNext !=NULL)
    {
        tpt_st_snakeT = tpt_st_snake->st_snakeNext;
        free(tpt_st_snake);
        tpt_st_snake = tpt_st_snakeT;
    }
    endwin();
    return;
}
u1 fs_judge()
{
    u1 u1_rtnVal;
    struct st_snake* tpt_st_snake;
    
    tpt_st_snake = sst_snakeHead;
    u1_rtnVal = 0;
    
    /**if touch the edge of window gameovere**/
    if( (sst_snakeHead->u2_cols == 0)    ||
        (sst_snakeHead->u2_line == 0)    ||
        (sst_snakeHead->u2_cols == COLS) || 
        (sst_snakeHead->u2_line == LINES) )
    {
        u1_rtnVal = 1;
    }
    /**if snake eat itself**/
    tpt_st_snake = tpt_st_snake->st_snakeNext;
    while(tpt_st_snake->st_snakeNext != NULL)
    {
        if( (sst_snakeHead->u2_cols == tpt_st_snake->u2_cols) &&
            (sst_snakeHead->u2_line == tpt_st_snake->u2_line)   )
            {
                u1_rtnVal = 1;            
            }
        tpt_st_snake = tpt_st_snake->st_snakeNext;
    }
    /**If snake_head position == food**/
    if( (sst_snakeHead->u2_cols == su2_foodCols) &&
        (sst_snakeHead->u2_line == su2_foodLine)    )
    {
        fs_snake_add();
        beep();
    }

    return u1_rtnVal;
}
